home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / lib / system-service / system-service-d
Text File  |  2009-10-15  |  20KB  |  510 lines

  1. #!/usr/bin/python
  2.  
  3. # (c) 2008 Canonical Ltd.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along
  16. # with this program; if not, write to the Free Software Foundation, Inc.,
  17. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18.  
  19. import sys
  20. import gobject
  21. import dbus
  22. import dbus.service
  23. import dbus.mainloop.glib 
  24. import os
  25. import subprocess
  26. import apt_pkg
  27. import struct
  28. import fcntl
  29.  
  30. from UbuntuSystemService.utils import *
  31.  
  32. class UnknownProxyTypeError(dbus.DBusException):
  33.     " a unknown proxy type was passed "
  34.     pass
  35. class InvalidKeyboardTypeError(dbus.DBusException):
  36.     " a invalid keyboard was set "
  37.     pass
  38. class PermissionDeniedError(dbus.DBusException):
  39.     " permission denied by policy "
  40.     pass
  41.  
  42.  
  43. class ServiceBackend(dbus.service.Object): 
  44.     """ 
  45.     the main backend class that supports various system settings like
  46.     proxy and keyboard
  47.     """
  48.  
  49.     # some class properties
  50.     DBUS_INTERFACE_NAME = "com.ubuntu.SystemService"
  51.     SUPPORTED_PROXIES = ("http","ftp", "https", "socks")
  52.  
  53.     # default files
  54.     CONSOLE_SETUP_DEFAULT = "/etc/default/console-setup"
  55.     DPKG_LOCK = "/var/lib/dpkg/lock"
  56.  
  57.     def __init__(self):
  58.         bus_name = dbus.service.BusName(self.DBUS_INTERFACE_NAME,
  59.                                         bus=dbus.SystemBus())
  60.         dbus.service.Object.__init__(self, bus_name, '/')
  61.         apt_pkg.InitConfig()
  62.  
  63.     def _authWithPolicyKit(self, sender, connection, priv):
  64.         #print "_authWithPolicyKit()"
  65.         system_bus = dbus.SystemBus()
  66.         obj = system_bus.get_object("org.freedesktop.PolicyKit1", 
  67.                                     "/org/freedesktop/PolicyKit1/Authority", 
  68.                                     "org.freedesktop.PolicyKit1.Authority")
  69.         policykit = dbus.Interface(obj, "org.freedesktop.PolicyKit1.Authority")
  70.         info = dbus.Interface(connection.get_object('org.freedesktop.DBus',
  71.                                               '/org/freedesktop/DBus/Bus', 
  72.                                               False), 
  73.                               'org.freedesktop.DBus')
  74.         pid = info.GetConnectionUnixProcessID(sender) 
  75.         #print "pid is:",pid
  76.         #print "priv: ", priv
  77.         subject = ('unix-process', 
  78.                    { 'pid' : dbus.UInt32(pid, variant_level=1) } 
  79.                    )
  80.         details = { '' : '' }
  81.         flags = dbus.UInt32(1) #   AllowUserInteraction = 0x00000001
  82.         cancel_id = ''
  83.         (ok, notused, details) = policykit.CheckAuthorization(subject,
  84.                                                               priv, 
  85.                                                               details,
  86.                                                               flags,
  87.                                                               cancel_id)
  88.                                                               
  89.         #print "ok: ", ok
  90.         return ok
  91.  
  92.     # proxy stuff ---------------------------------------------------
  93.     def _etc_environment_proxy(self, proxy_type):
  94.         " internal that returns the /etc/environment proxy "
  95.         if not os.path.exists("/etc/environment"):
  96.             return ""
  97.         for line in open("/etc/environment"):
  98.             if line.startswith("%s_proxy=" % proxy_type):
  99.                 (key, value) = line.strip().split("=")
  100.                 value = value.strip('"')
  101.                 return value
  102.         return ""
  103.  
  104.     def _http_proxy(self):
  105.         " internal helper that returns the current http proxy "
  106.         apt_proxy = self._apt_proxy("http")
  107.         env_proxy = self._etc_environment_proxy("http")
  108.         # FIXME: what to do if both proxies are differnet?
  109.         return env_proxy
  110.  
  111.     def _apt_proxy(self, proxy_type):
  112.         " internal helper that returns the configured apt proxy"
  113.         apt_pkg.InitConfig()
  114.         proxy = apt_pkg.Config.Find("Acquire::%s::proxy" % proxy_type)
  115.         return proxy
  116.  
  117.     def _ftp_proxy(self):
  118.         apt_proxy = self._apt_proxy("ftp")
  119.         env_proxy = self._etc_environment_proxy("ftp")
  120.         # FIXME: what to do if both proxies are differnet?
  121.         return env_proxy
  122.  
  123.     def _socks_proxy(self):
  124.         env_proxy = self._etc_environment_proxy("socks")
  125.         return env_proxy
  126.  
  127.     def _ftp_apt_proxy(self):
  128.         " internal helper that returns the configured apt proxy"
  129.         apt_pkg.InitConfig()
  130.         http_proxy = apt_pkg.Config.Find("Acquire::ftp::proxy")
  131.         return http_proxy
  132.  
  133.     def _https_proxy(self):
  134.         " internal helper that returns the current https proxy "
  135.         env_proxy = self._etc_environment_proxy("https")
  136.         return env_proxy
  137.  
  138.     def _verify_proxy(self, proxy_type, proxy):
  139.         " internal helper, verify that the proxy string is valid "
  140.         return verify_proxy(proxy_type, proxy)
  141.  
  142.     def _verify_no_proxy(self, proxy):
  143.         " internal helper, verify that the no_proxy string is valid "
  144.         return verify_no_proxy(proxy)
  145.  
  146.     @dbus.service.method(DBUS_INTERFACE_NAME,
  147.                          in_signature='s', 
  148.                          out_signature='s',
  149.                          sender_keyword='sender',
  150.                          connection_keyword='conn')
  151.     def get_proxy(self, proxy_type, sender=None, conn=None):
  152.         """ 
  153.         Get the current system-wide proxy  for type "proxy_type"
  154.  
  155.         This function will look in the apt configuration to 
  156.         find the current http proxy.
  157.         """
  158.         if proxy_type == "http":
  159.             return self._http_proxy()
  160.         if proxy_type == "https":
  161.             return self._https_proxy()
  162.         elif proxy_type == "ftp": 
  163.             return self._ftp_proxy()
  164.         elif proxy_type == "socks": 
  165.             return self._socks_proxy()
  166.         raise UnknownProxyTypeError, "proxy_type '%s' is unknown in get_proxy" % proxy_type
  167.  
  168.  
  169.     def _write_apt_proxy(self, proxy_type, new_proxy):
  170.         " helper that writes the new apt proxy "
  171.         confdir = apt_pkg.Config.FindDir("Dir::Etc") 
  172.         if not self._verify_proxy(proxy_type, new_proxy):
  173.             return False
  174.         # check for the easy case (no proxy setting in the config)
  175.         old_proxy = self._apt_proxy(proxy_type)
  176.         if old_proxy == "":
  177.             f=open(os.path.join(confdir, "apt.conf"),"a")
  178.             f.write("Acquire::%s::proxy \"%s\";\n" % (proxy_type, new_proxy))
  179.             f.close()
  180.             return True
  181.         # now the difficult case (search the apt configuration files)
  182.         # build the list of apt configuration files first
  183.         apt_conffiles = [os.path.join(confdir,"apt.conf.d",n) for n in 
  184.                          os.listdir(os.path.join(confdir,"apt.conf.d"))]
  185.         apt_conffiles.insert(0, os.path.join(confdir,"apt.conf"))
  186.         # then scan them for the content
  187.         for f in apt_conffiles:
  188.             new_content = []
  189.             found = False
  190.             for line in open(f):
  191.                 if line.lower().startswith("acquire::%s::proxy" % proxy_type):
  192.                     found = True
  193.                     line = "Acquire::%s::proxy \"%s\";\n" % (proxy_type, new_proxy)
  194.                 # FIXME: scan for more complicated forms of the proxy
  195.                 # settings and/or scan for the proxy string and just
  196.                 # replace this
  197.                 new_content.append(line)
  198.             # if we found/replaced the proxy, write it out now
  199.             if found:
  200.                 open(f,"w").write("".join(new_content))
  201.                 return True
  202.         return False
  203.  
  204.     def _write_etc_environment_proxy(self, proxy_type, new_proxy):
  205.         if not self._verify_proxy(proxy_type, new_proxy):
  206.             return False
  207.         found=False
  208.         new_content=[]
  209.         new_proxy_line = '%s_proxy="%s"\n' % (proxy_type, new_proxy)
  210.         for line in open("/etc/environment"):
  211.             if line.startswith("%s_proxy=" % proxy_type):
  212.                 line=new_proxy_line
  213.                 found = True
  214.             new_content.append(line)
  215.         if found:
  216.             open("/etc/environment","w").write("".join(new_content))
  217.         else:
  218.             open("/etc/environment","a").write(new_proxy_line)
  219.         return True
  220.  
  221.     def _clear_etc_environment_proxy(self, proxy_type):
  222.         found=False
  223.         new_content=[]
  224.         for line in open("/etc/environment"):
  225.             if line.startswith("%s_proxy=" % proxy_type):
  226.                 found = True
  227.             else:
  228.                 new_content.append(line)
  229.         if found:
  230.             open("/etc/environment","w").write("".join(new_content))
  231.         return True
  232.     
  233.     def _clear_apt_proxy(self, proxy_type):
  234.         " helper that clears the apt proxy "
  235.         confdir = apt_pkg.Config.FindDir("Dir::Etc") 
  236.         apt_conffiles = [os.path.join(confdir,"apt.conf.d",n) for n in 
  237.                          os.listdir(os.path.join(confdir,"apt.conf.d"))]
  238.         apt_conffiles.insert(0, os.path.join(confdir,"apt.conf"))
  239.         for f in apt_conffiles:
  240.             new_content = []
  241.             found = False
  242.             for line in open(f):
  243.                 if line.lower().startswith("acquire::%s::proxy" % proxy_type):
  244.                     found = True
  245.                 else:
  246.                     new_content.append(line)
  247.             # if we found/replaced the proxy, write it out now
  248.             if found:
  249.                 open(f,"w").write("".join(new_content))
  250.         return True
  251.     
  252.     @dbus.service.method(DBUS_INTERFACE_NAME,
  253.                          in_signature='ss', 
  254.                          out_signature='b',
  255.                          sender_keyword='sender',
  256.                          connection_keyword='conn')
  257.     def set_proxy(self, proxy_type, new_proxy, sender=None, conn=None):
  258.         """
  259.         Set a new system-wide proxy that looks like e.g.:
  260.         http://proxy.host.net:port/
  261.  
  262.         This function will set a new apt configuration and
  263.         modify /etc/environment
  264.         
  265.         """
  266.         if not self._authWithPolicyKit(sender, conn, 
  267.                                        "com.ubuntu.systemservice.setproxy"):
  268.             if not self._authWithPolicyKit(sender, conn,
  269.                                            "org.gnome.gconf.defaults.set-system"):
  270.                 raise PermissionDeniedError, "Permission denied by policy"
  271.         
  272.         # check if something supported is set
  273.         if not proxy_type in self.SUPPORTED_PROXIES:
  274.             raise UnknownProxyTypeError, "proxy_type '%s' is unknown in set_proxy" % proxy_type
  275.         
  276.         # set (or reset)
  277.         if new_proxy == "" or new_proxy is None:
  278.             res = self._clear_apt_proxy(proxy_type)
  279.             res &= self._clear_etc_environment_proxy(proxy_type)
  280.         else:
  281.             res = self._write_apt_proxy(proxy_type, new_proxy)
  282.             res &= self._write_etc_environment_proxy(proxy_type, new_proxy)
  283.         return res
  284.  
  285.  
  286.     def _clear_etc_environment_no_proxy(self):
  287.         found=False
  288.         new_content=[]
  289.         for line in open("/etc/environment"):
  290.             if line.startswith("no_proxy="):
  291.                 found = True
  292.             else:
  293.                 new_content.append(line)
  294.         if found:
  295.             open("/etc/environment","w").write("".join(new_content))
  296.         return True
  297.  
  298.     def _write_etc_environment_no_proxy(self, new_proxy):
  299.         if not self._verify_no_proxy(new_proxy):
  300.             return False
  301.         found=False
  302.         new_content=[]
  303.         new_proxy_line = 'no_proxy="%s"\n' % new_proxy
  304.         for line in open("/etc/environment"):
  305.             if line.startswith("no_proxy="):
  306.                 line=new_proxy_line
  307.                 found = True
  308.             new_content.append(line)
  309.         if found:
  310.             open("/etc/environment","w").write("".join(new_content))
  311.         else:
  312.             open("/etc/environment","a").write(new_proxy_line)
  313.         return True
  314.  
  315.     @dbus.service.method(DBUS_INTERFACE_NAME,
  316.                          in_signature='s', 
  317.                          out_signature='b',
  318.                          sender_keyword='sender',
  319.                          connection_keyword='conn')
  320.     def set_no_proxy(self, new_no_proxy, sender=None, conn=None):
  321.         """
  322.         Set a new system-wide no_proxy list that looks like e.g.:
  323.         localhost,foo.com
  324.  
  325.         This function will modify /etc/environment
  326.         
  327.         """
  328.         if not self._authWithPolicyKit(sender, conn, 
  329.                                        "com.ubuntu.systemservice.setnoproxy"):
  330.             if not self._authWithPolicyKit(sender, conn,
  331.                                            "org.gnome.gconf.defaults.set-system"):
  332.                 raise PermissionDeniedError, "Permission denied by policy"
  333.         
  334.         # set (or reset)
  335.         if new_no_proxy == "" or new_no_proxy is None:
  336.             res = self._clear_no_proxy()
  337.         else:
  338.             res = self._write_etc_environment_no_proxy(new_no_proxy)
  339.         return res
  340.  
  341.     # keyboard stuff ---------------------------------------------------
  342.     def _get_keyboard_from_etc(self):
  343.         """ 
  344.         helper that reads /etc/default/console-setup and gets the 
  345.         keyboard settings there
  346.         """
  347.         model = ""
  348.         layout = ""
  349.         variant = ""
  350.         options = ""
  351.         for line in open(self.CONSOLE_SETUP_DEFAULT):
  352.             if line.startswith("XKBMODEL="):
  353.                 model = line.split("=")[1].strip('"\n')
  354.             elif line.startswith("XKBLAYOUT="):
  355.                 layout = line.split("=")[1].strip('"\n')
  356.             elif line.startswith("XKBVARIANT="):
  357.                 variant = line.split("=")[1].strip('"\n')
  358.             elif line.startswith("XKBOPTIONS="):
  359.                 options = line.split("=")[1].strip('"\n')
  360.         return (model, layout, variant, options)
  361.  
  362.     @dbus.service.method(DBUS_INTERFACE_NAME,
  363.                          in_signature='', 
  364.                          out_signature='ssss',
  365.                          sender_keyword='sender',
  366.                          connection_keyword='conn')
  367.     def get_keyboard(self, sender=None, conn=None):
  368.         """
  369.         Set the system default keyboard configuration. 
  370.  
  371.         It expects four input arguments (strings):
  372.         model -- the model (evdev, pc105, ...)
  373.         layout -- the layout (de, us, ...)
  374.         variant -- the variant (nodeadkeys, ..)
  375.         options -- keyboard options (nocaps, ...)
  376.  
  377.         It returns True on sucess
  378.         """
  379.         (model, layout, variant, options) = self._get_keyboard_from_etc()
  380.         return (model, layout, variant, options)
  381.  
  382.     def _set_keyboard_to_etc(self, model, layout, variant, options):
  383.         """ 
  384.         helper that writes /etc/default/console-setup 
  385.         """
  386.         #print "set_keyboard_to_etc"
  387.         # FIXME: what to do if not os.path.exists(self.CONSOLE_SETUP_DEFAULT)
  388.         content = []
  389.         for line in open(self.CONSOLE_SETUP_DEFAULT):
  390.             if line.startswith("XKBMODEL="):
  391.                 line = 'XKBMODEL="%s"\n' % model
  392.             elif line.startswith("XKBLAYOUT="):
  393.                 line = 'XKBLAYOUT="%s"\n' % layout
  394.             elif line.startswith("XKBVARIANT="):
  395.                 line = 'XKBVARIANT="%s"\n' % variant
  396.             elif line.startswith("XKBOPTIONS="):
  397.                 line = 'XKBOPTIONS="%s"\n' % options
  398.             content.append(line)
  399.         # if something changed, write 
  400.         if content != open(self.CONSOLE_SETUP_DEFAULT).readlines():
  401.             #print "content changed, writing"
  402.             open(self.CONSOLE_SETUP_DEFAULT+".new","w").write("".join(content))
  403.             os.rename(self.CONSOLE_SETUP_DEFAULT+".new", 
  404.                       self.CONSOLE_SETUP_DEFAULT)
  405.         return True
  406.  
  407.     def _verify_keyboard_settings(self, model, layout, variant, options):
  408.         " helper that verfies the settings "
  409.         # check against char whitelist
  410.         allowed = "^[0-9a-zA-Z:,_]*$"
  411.         for s in (model, layout, variant, options):
  412.             if not re.match(allowed, s):
  413.                 #print "illegal chars in '%s'" % s
  414.                 return False
  415.         # check if 'ckbcomp' can compile it
  416.         cmd = ["ckbcomp"]
  417.         if model:
  418.             cmd += ["-model",model]
  419.         if layout:
  420.             cmd += ["-layout", layout]
  421.         if variant:
  422.             cmd += ["-variant", variant]
  423.         if options:
  424.             cmd += ["-option", options]
  425.         ret = subprocess.call(cmd, stdout=open(os.devnull))
  426.         return (ret == 0)
  427.  
  428.     def _run_setupcon(self):
  429.         """
  430.         helper that runs setupcon to activate the settings, taken from 
  431.         oem-config (/usr/lib/oem-config/console/console-setup-apply)
  432.         """
  433.         ret = subprocess.call(["setupcon","--save-only"])
  434.         subprocess.Popen(["/usr/sbin/update-initramfs","-u"])
  435.         return (ret == 0)
  436.  
  437.     @dbus.service.method(DBUS_INTERFACE_NAME,
  438.                          in_signature='ssss', 
  439.                          out_signature='b',
  440.                          sender_keyword='sender',
  441.                          connection_keyword='conn')
  442.     def set_keyboard(self, model, layout, variant, options, sender=None, conn=None):
  443.         """
  444.         Get the current keyboard configuration. This returns four
  445.         strings: (model, layout, variant, options)
  446.         """
  447.         #print "set_keyboard: ", model, layout, variant, options
  448.         if not self._authWithPolicyKit(sender, conn, 
  449.                                        "com.ubuntu.systemservice.setkeyboard"):
  450.             if not self._authWithPolicyKit(sender, conn,
  451.                                            "org.gnome.gconf.defaults.set-system"):
  452.  
  453.                 raise PermissionDeniedError, "Permission denied by policy"
  454.  
  455.         # if no keyboard model is set, try to guess one
  456.         # this is based on the "console-setup.config" code that
  457.         # defaults to pc105
  458.         if not model:
  459.             model = "pc105"
  460.             if layout == "us":
  461.                 model = "pc104"
  462.             elif layout == "br":
  463.                 model = "abnt2"
  464.             elif layout == "jp":
  465.                 model = "jp106"
  466.  
  467.         # verify the settings
  468.         if not self._verify_keyboard_settings(model, layout, variant, options):
  469.             #print "verify_keyboard failed"
  470.             raise InvalidKeyboardTypeError, "Invalid keyboard set"
  471.         
  472.         # apply
  473.         if not self._set_keyboard_to_etc(model, layout, variant, options):
  474.             #print "could not write keyboard to /etc"
  475.             return False
  476.         if not self._run_setupcon():
  477.             #print "setupcon failed"
  478.             return False
  479.         return True
  480.  
  481.     @dbus.service.method(DBUS_INTERFACE_NAME,
  482.                          in_signature='', 
  483.                          out_signature='b',
  484.                          sender_keyword='sender',
  485.                          connection_keyword='conn')
  486.     def is_package_system_locked(self, sender=None, conn=None):
  487.         """
  488.         Check if the package system is locked
  489.         """
  490.         #print "set_keyboard: ", model, layout, variant, options
  491.         if not self._authWithPolicyKit(sender, conn, 
  492.                                        "com.ubuntu.systemservice.ispkgsystemlocked"):
  493.                 raise PermissionDeniedError, "Permission denied by policy"
  494.         # check for file
  495.         if not os.path.exists(self.DPKG_LOCK):
  496.             return True
  497.         # check for lock
  498.         flk=struct.pack('hhllhl',fcntl.F_WRLCK,0,0,0,0,0)
  499.         f=open(self.DPKG_LOCK)
  500.         rv = fcntl.fcntl(f, fcntl.F_GETLK, flk)
  501.         lockv = struct.unpack('hhllhl', rv)[0]
  502.         f.close()
  503.         return (lockv == fcntl.F_WRLCK)
  504.  
  505. if __name__ == "__main__":
  506.     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 
  507.     server = ServiceBackend()
  508.     gobject.MainLoop().run()
  509.  
  510.